//
// Created by wxd on 2025/8/1.
//


#pragma once

#ifndef RENDERENGINE_LOGGER_H
#define RENDERENGINE_LOGGER_H

#include <iostream>
#include <sstream>
#include <unordered_map>
#include <chrono>
#include <string>
#include <ctime>
#include <iomanip>


class ILogger {
public:
    virtual ~ILogger() = default;
    virtual void v() = 0;
    virtual void i() = 0;
    virtual void d() = 0;
    virtual void w() = 0;
    virtual void e() = 0;
};

class RenderEngineLogger : public ILogger {
public:
    enum LogLevel {
        _VERBOSE, _INFO, _DEBUG, _WARN, _ERROR
    };

    RenderEngineLogger() = default;
    ~RenderEngineLogger() {

    }

    RenderEngineLogger& msg(const std::string& tag) {
        m_TAG = tag;
        m_LogInfoMap.clear();
        return *this;
    }

    RenderEngineLogger& param(const std::string& key, const std::string& value) {
        m_LogInfoMap[key] = value;
        return *this;
    }

    void v() override {
        log(_VERBOSE, m_TAG, m_LogInfoMap);
    }

    void i() override {
        log(_INFO, m_TAG, m_LogInfoMap);
    }

    void d() override {
        log(_DEBUG, m_TAG, m_LogInfoMap);
    }

    void w() override {
        log(_WARN, m_TAG, m_LogInfoMap);
    }

    void e() override {
        log(_ERROR, m_TAG, m_LogInfoMap);
    }
private:
    static std::unordered_map<std::string, std::string> m_LogInfoMap;
    static std::string m_TAG;

    static void log(LogLevel level, const std::string& tag, const std::unordered_map<std::string, std::string>& params) {
        std::string levelStr;
        switch (level) {
            case _VERBOSE: levelStr = "V"; break;
            case _DEBUG:   levelStr = "D"; break;
            case _INFO:    levelStr = "I"; break;
            case _WARN:    levelStr = "W"; break;
            case _ERROR:   levelStr = "E"; break;
        }

        auto now = std::chrono::system_clock::now();
        auto now_time = std::chrono::system_clock::to_time_t(now);
        std::tm now_tm = *std::localtime(&now_time);
        std::ostringstream timeStream;
        timeStream << std::put_time(&now_tm, "%Y-%m-%d %H:%M:%S");

        std::ostringstream logStream;
        logStream << "[" << timeStream.str() << "] "
                 << levelStr << "/" << tag << ": ";

        for (const auto& [key, value] : params) {
            logStream << key << " = " << value << "; ";
        }

        std::cout << logStream.str() << "\n";
    }

    static void v(const std::string& tag, const std::string& message) {
        auto now = std::chrono::system_clock::now();
        auto now_time = std::chrono::system_clock::to_time_t(now);
        std::tm now_tm = *std::localtime(&now_time);
        std::ostringstream timeStream;
        timeStream << std::put_time(&now_tm, "%Y-%m-%d %H:%M:%S");

        std::ostringstream logStream;
        logStream << "[" << timeStream.str() << "] "
                 << "V" << "/" << tag << ": ";

        logStream << message;

        std::cout << logStream.str() << "\n";
    }

    static void i(const std::string& tag, const std::string& message) {
        auto now = std::chrono::system_clock::now();
        auto now_time = std::chrono::system_clock::to_time_t(now);
        std::tm now_tm = *std::localtime(&now_time);
        std::ostringstream timeStream;
        timeStream << std::put_time(&now_tm, "%Y-%m-%d %H:%M:%S");

        std::ostringstream logStream;
        logStream << "[" << timeStream.str() << "] "
                 << "I" << "/" << tag << ": ";

        logStream << message;

        std::cout << logStream.str() << "\n";
    }

    static void d(const std::string& tag, const std::string& message) {
        auto now = std::chrono::system_clock::now();
        auto now_time = std::chrono::system_clock::to_time_t(now);
        std::tm now_tm = *std::localtime(&now_time);
        std::ostringstream timeStream;
        timeStream << std::put_time(&now_tm, "%Y-%m-%d %H:%M:%S");

        std::ostringstream logStream;
        logStream << "[" << timeStream.str() << "] "
                 << "D" << "/" << tag << ": ";

        logStream << message;

        std::cout << logStream.str() << "\n";
    }

    static void w(const std::string& tag, const std::string& message) {
        auto now = std::chrono::system_clock::now();
        auto now_time = std::chrono::system_clock::to_time_t(now);
        std::tm now_tm = *std::localtime(&now_time);
        std::ostringstream timeStream;
        timeStream << std::put_time(&now_tm, "%Y-%m-%d %H:%M:%S");

        std::ostringstream logStream;
        logStream << "[" << timeStream.str() << "] "
                 << "W" << "/" << tag << ": ";

        logStream << message;

        std::cout << logStream.str() << "\n";
    }

    static void e(const std::string& tag, const std::string& message) {
        auto now = std::chrono::system_clock::now();
        auto now_time = std::chrono::system_clock::to_time_t(now);
        std::tm now_tm = *std::localtime(&now_time);
        std::ostringstream timeStream;
        timeStream << std::put_time(&now_tm, "%Y-%m-%d %H:%M:%S");

        std::ostringstream logStream;
        logStream << "[" << timeStream.str() << "] "
                 << "E" << "/" << tag << ": ";

        logStream << message;

        std::cout << logStream.str() << "\n";
    }
};

inline std::string RenderEngineLogger::m_TAG = "";
inline std::unordered_map<std::string, std::string> RenderEngineLogger::m_LogInfoMap;

inline RenderEngineLogger Log;

#endif